{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "48dac6da-6fdd-4ce2-a38d-0fef5ab1350c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([0, 1, 2,  ..., 6, 1, 0])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "torch.Size([1024, 1, 28, 28])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import os\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets,transforms\n",
    "from  torch import nn,optim\n",
    "\n",
    "import ssl\n",
    "ssl._create_default_https_context = ssl._create_unverified_context\n",
    "\n",
    "batch_size=1024\n",
    "mnis_dir=os.getenv('HOME')+\"/SUFE/mnist\"\n",
    "# 数据加载\n",
    "mnist_train=datasets.MNIST(mnis_dir,True,\n",
    "                           transform=transforms.Compose([transforms.ToTensor()]),\n",
    "                           download=False)\n",
    "mnist_train=DataLoader(mnist_train,batch_size=batch_size,shuffle=True)\n",
    "minst_test=datasets.MNIST(mnis_dir,False,transform=transforms.Compose([\n",
    "    transforms.ToTensor() ]),download=False)\n",
    "minst_test=DataLoader(minst_test,batch_size=batch_size,shuffle=True)\n",
    "x,lable=next(iter(mnist_train))\n",
    "print(lable)\n",
    "x.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "18d80e8c-93a3-4694-819b-609a4b7e1a7d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.12.5 (v3.12.5:ff3bc82f7c9, Aug  7 2024, 05:32:06) [Clang 13.0.0 (clang-1300.0.29.30)]\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "print(sys.version)\n",
    "device=torch.device(\"mps\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "e0d66154-0a60-4570-b26c-09b7c92e518d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 自编码器\n",
    "class AE(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(AutoEncoder, self).__init__()\n",
    "        self.encoder = nn.Sequential(\n",
    "            nn.Linear(3, 12),\n",
    "            nn.LeakyReLU(),\n",
    "            nn.Linear(12, 48),\n",
    "            nn.LeakyReLU(),\n",
    "\n",
    "        )\n",
    "        self.decoder = nn.Sequential(\n",
    "            nn.Linear(48, 12),\n",
    "            nn.LeakyReLU(),\n",
    "            nn.Linear(12, 3),\n",
    "            nn.Tanh()\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        encoded = self.encoder(x)\n",
    "        decoded = self.decoder(encoded)\n",
    "        return encoded, decoded"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "9a58c832-a235-448c-a843-79946f0c4f26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequential(\n",
      "  (conv1): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1))\n",
      "  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (conv2): Conv2d(64, 512, kernel_size=(3, 3), stride=(1, 1))\n",
      "  (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (dropout): Dropout2d(p=0.1, inplace=False)\n",
      "  (adaptive_pool): AdaptiveMaxPool2d(output_size=(1, 1))\n",
      "  (flatten): Flatten(start_dim=1, end_dim=-1)\n",
      "  (linear1): Linear(in_features=512, out_features=1024, bias=True)\n",
      "  (relu): ReLU()\n",
      "  (linear2): Linear(in_features=1024, out_features=10, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "# define model\n",
    "def create_net():\n",
    "    net = nn.Sequential()\n",
    "    net.add_module(\"conv1\",nn.Conv2d(in_channels=1,out_channels=64,kernel_size = 3))\n",
    "    net.add_module(\"pool1\",nn.MaxPool2d(kernel_size = 2,stride = 2))\n",
    "    net.add_module(\"conv2\",nn.Conv2d(in_channels=64,out_channels=512,kernel_size = 3))\n",
    "    net.add_module(\"pool2\",nn.MaxPool2d(kernel_size = 2,stride = 2))\n",
    "    net.add_module(\"dropout\",nn.Dropout2d(p = 0.1))\n",
    "    net.add_module(\"adaptive_pool\",nn.AdaptiveMaxPool2d((1,1)))\n",
    "    net.add_module(\"flatten\",nn.Flatten())\n",
    "    net.add_module(\"linear1\",nn.Linear(512,1024))\n",
    "    net.add_module(\"relu\",nn.ReLU())\n",
    "    net.add_module(\"linear2\",nn.Linear(1024,10))\n",
    "    return net\n",
    "\n",
    "model = create_net()\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "090d7d4e-cfd2-4ba9-9833-90ee6707e10c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Accuracy\n",
    "class Accuracy(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "\n",
    "        self.correct = nn.Parameter(torch.tensor(0.0),requires_grad=False)\n",
    "        self.total = nn.Parameter(torch.tensor(0.0),requires_grad=False)\n",
    "\n",
    "    def forward(self, preds: torch.Tensor, targets: torch.Tensor):\n",
    "        preds = preds.argmax(dim=-1)\n",
    "        m = (preds == targets).sum()\n",
    "        n = targets.shape[0] \n",
    "        self.correct += m \n",
    "        self.total += n\n",
    "        \n",
    "        return m/n\n",
    "\n",
    "    def compute(self):\n",
    "        return self.correct.float() / self.total \n",
    "    \n",
    "    def reset(self):\n",
    "        self.correct -= self.correct\n",
    "        self.total -= self.total\n",
    "\n",
    "\n",
    "#train model\n",
    "loss_fn = nn.CrossEntropyLoss()\n",
    "optimizer= torch.optim.Adam(model.parameters(),lr = 0.01)   \n",
    "metrics_dict = nn.ModuleDict({\"acc\":Accuracy()})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "465c6e37-e61f-4410-afeb-e55f9c248a24",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ModuleDict(\n",
       "  (acc): Accuracy()\n",
       ")"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# =========================移动模型到mps上==============================\n",
    "device = torch.device(\"mps\" if torch.backends.mps.is_available() else \"cpu\")\n",
    "model.to(device)\n",
    "loss_fn.to(device)\n",
    "metrics_dict.to(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "d4a40d3d-e24b-4e43-aa4c-ba1db7c4fe66",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm import tqdm \n",
    "from copy import deepcopy\n",
    "from torchmetrics import Accuracy\n",
    "import datetime \n",
    "\n",
    "\n",
    "def printlog(info):\n",
    "    nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')\n",
    "    print(\"\\n\"+\"==========\"*8 + \"%s\"%nowtime)\n",
    "    print(str(info)+\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "aec5b4de-aa4e-4611-a9b2-08c6dba1c7ad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:39:30\n",
      "Epoch 1 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:31<00:00,  1.87it/s, train_acc=0.986, train_loss=0.0428]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.31it/s, val_acc=0.983, val_loss=0.0548]\n",
      "<<<<<< reach best val_acc : 0.983299970626831 >>>>>>\n",
      "/var/folders/mh/l6_b7c0x7m3bq41r5m25snqc0000gn/T/ipykernel_26158/3338308172.py:125: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(ckpt_path))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:40:04\n",
      "Epoch 2 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:31<00:00,  1.89it/s, train_acc=0.987, train_loss=0.0398]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.20it/s, val_acc=0.983, val_loss=0.0558]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:40:36\n",
      "Epoch 3 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████████████████████████| 59/59 [00:32<00:00,  1.84it/s, train_acc=0.71, train_loss=9.95]\n",
      "100%|████████████████████████████████| 10/10 [00:01<00:00,  6.39it/s, val_acc=0.0813, val_loss=2.27]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:41:10\n",
      "Epoch 4 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|████████████████████████████| 59/59 [00:33<00:00,  1.77it/s, train_acc=0.944, train_loss=0.176]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.49it/s, val_acc=0.975, val_loss=0.0837]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:41:45\n",
      "Epoch 5 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:32<00:00,  1.83it/s, train_acc=0.994, train_loss=0.0208]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.24it/s, val_acc=0.985, val_loss=0.0415]\n",
      "<<<<<< reach best val_acc : 0.9854999780654907 >>>>>>\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:42:19\n",
      "Epoch 6 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|████████████████████████████| 59/59 [00:31<00:00,  1.85it/s, train_acc=0.996, train_loss=0.016]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.48it/s, val_acc=0.988, val_loss=0.0418]\n",
      "<<<<<< reach best val_acc : 0.9876000285148621 >>>>>>\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:42:52\n",
      "Epoch 7 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:33<00:00,  1.78it/s, train_acc=0.996, train_loss=0.0137]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.27it/s, val_acc=0.986, val_loss=0.0409]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:43:27\n",
      "Epoch 8 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:31<00:00,  1.89it/s, train_acc=0.996, train_loss=0.0137]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.29it/s, val_acc=0.987, val_loss=0.0398]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:44:00\n",
      "Epoch 9 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:31<00:00,  1.89it/s, train_acc=0.996, train_loss=0.0141]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  5.76it/s, val_acc=0.987, val_loss=0.0391]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:44:33\n",
      "Epoch 10 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:33<00:00,  1.76it/s, train_acc=0.996, train_loss=0.0141]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.84it/s, val_acc=0.986, val_loss=0.0408]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "================================================================================2024-09-06 10:45:08\n",
      "Epoch 11 / 20\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|███████████████████████████| 59/59 [00:31<00:00,  1.85it/s, train_acc=0.996, train_loss=0.0138]\n",
      "100%|███████████████████████████████| 10/10 [00:01<00:00,  6.17it/s, val_acc=0.986, val_loss=0.0404]\n",
      "<<<<<< val_acc without improvement in 5 epoch, early stopping >>>>>>\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "epochs = 20 \n",
    "ckpt_path='checkpoint.pt'\n",
    "\n",
    "#early_stopping相关设置\n",
    "monitor=\"val_acc\"\n",
    "patience=5\n",
    "mode=\"max\"\n",
    "\n",
    "history = {}\n",
    "\n",
    "for epoch in range(1, epochs+1):\n",
    "    printlog(\"Epoch {0} / {1}\".format(epoch, epochs))\n",
    "\n",
    "    # 1，train -------------------------------------------------  \n",
    "    model.train()\n",
    "    \n",
    "    total_loss,step = 0,0\n",
    "    \n",
    "    loop = tqdm(enumerate(mnist_train), total =len(mnist_train),ncols=100)\n",
    "    train_metrics_dict = deepcopy(metrics_dict) \n",
    "    \n",
    "    for i, batch in loop: \n",
    "        \n",
    "        features,labels = batch\n",
    "        \n",
    "        # =========================移动数据到mps上==============================\n",
    "        features = features.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # ====================================================================\n",
    "        \n",
    "        #forward\n",
    "        preds = model(features)\n",
    "        loss = loss_fn(preds,labels)\n",
    "        \n",
    "        #backward\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        optimizer.zero_grad()\n",
    "            \n",
    "        #metrics\n",
    "        step_metrics = {\"train_\"+name:metric_fn(preds, labels).item() \n",
    "                        for name,metric_fn in train_metrics_dict.items()}\n",
    "        \n",
    "        step_log = dict({\"train_loss\":loss.item()},**step_metrics)\n",
    "\n",
    "        total_loss += loss.item()\n",
    "        \n",
    "        step+=1\n",
    "        if i!=len(mnist_train)-1:\n",
    "            loop.set_postfix(**step_log)\n",
    "        else:\n",
    "            epoch_loss = total_loss/step\n",
    "            epoch_metrics = {\"train_\"+name:metric_fn.compute().item() \n",
    "                             for name,metric_fn in train_metrics_dict.items()}\n",
    "            epoch_log = dict({\"train_loss\":epoch_loss},**epoch_metrics)\n",
    "            loop.set_postfix(**epoch_log)\n",
    "\n",
    "            for name,metric_fn in train_metrics_dict.items():\n",
    "                metric_fn.reset()\n",
    "                \n",
    "    for name, metric in epoch_log.items():\n",
    "        history[name] = history.get(name, []) + [metric]\n",
    "        \n",
    "\n",
    "    # 2，validate -------------------------------------------------\n",
    "    model.eval()\n",
    "    \n",
    "    total_loss,step = 0,0\n",
    "    loop = tqdm(enumerate(minst_test), total =len(minst_test),ncols=100)\n",
    "    \n",
    "    val_metrics_dict = deepcopy(metrics_dict) \n",
    "    \n",
    "    with torch.no_grad():\n",
    "        for i, batch in loop: \n",
    "\n",
    "            features,labels = batch\n",
    "            \n",
    "            # =========================移动数据到mps上==============================\n",
    "            features = features.to(device)\n",
    "            labels = labels.to(device)\n",
    "            # ====================================================================\n",
    "            \n",
    "            #forward\n",
    "            preds = model(features)\n",
    "            loss = loss_fn(preds,labels)\n",
    "\n",
    "            #metrics\n",
    "            step_metrics = {\"val_\"+name:metric_fn(preds, labels).item() \n",
    "                            for name,metric_fn in val_metrics_dict.items()}\n",
    "\n",
    "            step_log = dict({\"val_loss\":loss.item()},**step_metrics)\n",
    "\n",
    "            total_loss += loss.item()\n",
    "            step+=1\n",
    "            if i!=len(minst_test)-1:\n",
    "                loop.set_postfix(**step_log)\n",
    "            else:\n",
    "                epoch_loss = (total_loss/step)\n",
    "                epoch_metrics = {\"val_\"+name:metric_fn.compute().item() \n",
    "                                 for name,metric_fn in val_metrics_dict.items()}\n",
    "                epoch_log = dict({\"val_loss\":epoch_loss},**epoch_metrics)\n",
    "                loop.set_postfix(**epoch_log)\n",
    "\n",
    "                for name,metric_fn in val_metrics_dict.items():\n",
    "                    metric_fn.reset()\n",
    "                    \n",
    "    epoch_log[\"epoch\"] = epoch           \n",
    "    for name, metric in epoch_log.items():\n",
    "        history[name] = history.get(name, []) + [metric]\n",
    "\n",
    "    # 3，early-stopping -------------------------------------------------\n",
    "    arr_scores = history[monitor]\n",
    "    best_score_idx = np.argmax(arr_scores) if mode==\"max\" else np.argmin(arr_scores)\n",
    "    if best_score_idx==len(arr_scores)-1:\n",
    "        torch.save(model.state_dict(),ckpt_path)\n",
    "        print(\"<<<<<< reach best {0} : {1} >>>>>>\".format(monitor,\n",
    "             arr_scores[best_score_idx]),file=sys.stderr)\n",
    "    if len(arr_scores)-best_score_idx>patience:\n",
    "        print(\"<<<<<< {} without improvement in {} epoch, early stopping >>>>>>\".format(\n",
    "            monitor,patience),file=sys.stderr)\n",
    "        break \n",
    "    model.load_state_dict(torch.load(ckpt_path))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "30babeb6-4dc2-4fbf-8847-a8c7c450774b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>train_loss</th>\n",
       "      <th>train_acc</th>\n",
       "      <th>val_loss</th>\n",
       "      <th>val_acc</th>\n",
       "      <th>epoch</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.042787</td>\n",
       "      <td>0.986300</td>\n",
       "      <td>0.054796</td>\n",
       "      <td>0.9833</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.039767</td>\n",
       "      <td>0.987017</td>\n",
       "      <td>0.055838</td>\n",
       "      <td>0.9828</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>9.948073</td>\n",
       "      <td>0.710483</td>\n",
       "      <td>2.270791</td>\n",
       "      <td>0.0813</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.175787</td>\n",
       "      <td>0.944317</td>\n",
       "      <td>0.083660</td>\n",
       "      <td>0.9755</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.020832</td>\n",
       "      <td>0.994100</td>\n",
       "      <td>0.041488</td>\n",
       "      <td>0.9855</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0.016050</td>\n",
       "      <td>0.995517</td>\n",
       "      <td>0.041849</td>\n",
       "      <td>0.9876</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>0.013723</td>\n",
       "      <td>0.996000</td>\n",
       "      <td>0.040948</td>\n",
       "      <td>0.9862</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>0.013714</td>\n",
       "      <td>0.996183</td>\n",
       "      <td>0.039789</td>\n",
       "      <td>0.9872</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>0.014086</td>\n",
       "      <td>0.996050</td>\n",
       "      <td>0.039144</td>\n",
       "      <td>0.9872</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>0.014072</td>\n",
       "      <td>0.996233</td>\n",
       "      <td>0.040824</td>\n",
       "      <td>0.9864</td>\n",
       "      <td>10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>0.013781</td>\n",
       "      <td>0.996450</td>\n",
       "      <td>0.040384</td>\n",
       "      <td>0.9859</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    train_loss  train_acc  val_loss  val_acc  epoch\n",
       "0     0.042787   0.986300  0.054796   0.9833      1\n",
       "1     0.039767   0.987017  0.055838   0.9828      2\n",
       "2     9.948073   0.710483  2.270791   0.0813      3\n",
       "3     0.175787   0.944317  0.083660   0.9755      4\n",
       "4     0.020832   0.994100  0.041488   0.9855      5\n",
       "5     0.016050   0.995517  0.041849   0.9876      6\n",
       "6     0.013723   0.996000  0.040948   0.9862      7\n",
       "7     0.013714   0.996183  0.039789   0.9872      8\n",
       "8     0.014086   0.996050  0.039144   0.9872      9\n",
       "9     0.014072   0.996233  0.040824   0.9864     10\n",
       "10    0.013781   0.996450  0.040384   0.9859     11"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "dfhistory = pd.DataFrame(history)\n",
    "dfhistory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6249cf9c-b363-41e9-86bb-b7548e986f99",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
